from socket import socket, AF_INET, SOCK_STREAM, error as socket_error
from PyQt6.QtCore import QObject, pyqtSignal, QThread
from utils.exceptions import ConnectionError, SendDataError


class TcpClientModel(QObject):
    """TCP客户端核心模型"""
    connected = pyqtSignal()
    disconnected = pyqtSignal()
    data_received = pyqtSignal(bytes)
    error_occurred = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.client_socket = None
        self.is_connected = False
        self.receive_thread = None

    def connect_to_server(self, host: str, port: int) -> None:
        """建立TCP连接"""
        try:
            if not 0 < port <= 65535:
                raise ConnectionError("无效的端口号")

            self.client_socket = socket(AF_INET, SOCK_STREAM)
            self.client_socket.settimeout(5)
            self.client_socket.connect((host, port))

            self.is_connected = True
            self._start_receive_thread()
            self.connected.emit()

        except socket_error as e:
            self._cleanup()
            raise ConnectionError(f"连接失败: {str(e)}")
        except Exception as e:
            self._cleanup()
            raise ConnectionError(f"未知错误: {str(e)}")

    def _start_receive_thread(self):
        """启动接收线程"""
        self.receive_thread = ReceiveThread(self.client_socket)
        self.receive_thread.data_received.connect(self.data_received.emit)
        self.receive_thread.error_occurred.connect(self.error_occurred.emit)
        self.receive_thread.start()

    def send_data(self, data: bytes) -> None:
        """发送数据"""
        if not self.is_connected:
            raise SendDataError("未建立连接")
        try:
            self.client_socket.sendall(data)
        except socket_error as e:
            self.disconnect()
            raise SendDataError(f"发送失败: {str(e)}")

    def disconnect(self) -> None:
        """断开连接"""
        self._cleanup()
        self.disconnected.emit()

    def _cleanup(self):
        """清理资源"""
        self.is_connected = False
        if self.client_socket:
            try:
                self.client_socket.shutdown(2)
                self.client_socket.close()
            except:
                pass
        if self.receive_thread and self.receive_thread.isRunning():
            self.receive_thread.quit()


class ReceiveThread(QThread):
    """数据接收线程"""
    data_received = pyqtSignal(bytes)
    error_occurred = pyqtSignal(str)

    def __init__(self, sock: socket):
        super().__init__()
        self.socket = sock
        self.running = True

    def run(self):
        while self.running and self.socket.fileno() != -1:
            try:
                data = self.socket.recv(4096)
                if data:
                    self.data_received.emit(data)
                else:  # 空数据表示连接关闭
                    self.stop()
            except socket_error:
                self.stop()
            except Exception as e:
                self.error_occurred.emit(str(e))
                self.stop()

    def stop(self):
        self.running = False